package ink.ykb.configurer.zookeeper;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;

import ink.ykb.util.PlatformRuntimeException;

@Configuration
@Aspect
public class ZookeeperLockAopConf {
	
//		@Pointcut("@annotation(ink.ykb.configurer.zookeeper.ClusterLock)")  
//		private void zkLockMethod(){}//定义Lock的切入点  

		private Logger logger = LoggerFactory.getLogger(ZookeeperLockAopConf.class);  
		
		@Autowired
		private CuratorFramework zkClient;
		
		
//		@Around("zkLockMethod()")
		@Around("@annotation(clusterLock)")
		public Object zkLockAop(ProceedingJoinPoint pjp,ClusterLock clusterLock) throws Throwable{
		
			Object o = null;
			InterProcessMutex lock = null;
			Object type = null;
			try {
				//可直接注入clusterLock
//		        Signature sig = pjp.getSignature();
//		        MethodSignature msig = (MethodSignature) sig;//代理方法
//		        Object target = pjp.getTarget();//代理类
//		        Method method = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
//		        ClusterLock clusterLock = method.getAnnotation(ClusterLock.class);
		        
		        if(clusterLock == null){throw new PlatformRuntimeException("未获取到注解："+ClusterLock.class.getName());}
		        
		    	String lockPath = clusterLock.value();
		    	String key = clusterLock.key();
		    	
		    	if(lockPath == null || lockPath.equals("")){throw new PlatformRuntimeException("zk锁路径不能为空");}
		    	if(key == null || key.equals("")){throw new PlatformRuntimeException("zk锁类型key不能为空");}
		    	
		    	Map<String, Object> parameterNames = this.getParameterNames(pjp);
		    	type = parameterNames.get(key);
		    	if(type == null){throw new PlatformRuntimeException("未获取到zk锁类型");}
		    	
				String path = lockPath+"/"+type.toString();
				
				lock = new InterProcessMutex(zkClient, path);
				
				if(lock.acquire(10, TimeUnit.HOURS)){
						logger.info("获取到zk锁："+type.toString());
						logger.info("开始执行任务");
				        o = pjp.proceed();//调用原方法
				        logger.info("执行任务结束");
				}
			} catch (Exception e) {
				logger.info("执行任务异常");
				throw e;
			}finally {
				if(lock != null){
					lock.release();
				}
				logger.info("释放zk锁："+type);
			}
			return o;
		}
		

		/**
		 * 获取方法的参数
		 * @param pjp
		 * @return
		 * @throws  
		 * @throws Exception 
		 */
		private Map<String, Object> getParameterNames(ProceedingJoinPoint pjp) throws Exception{
			
			String[] names = null;//参数名
			Object[] args = pjp.getArgs();//参数值
			
			Signature signature = pjp.getSignature();  
	        MethodSignature methodSignature = (MethodSignature) signature;  
			
			String jv = System.getProperty("java.version");
			String[] jvs = jv.split("\\.");
			if(Integer.parseInt(jvs[0]+jvs[1]) >= 18) {//jdk8直接获取参数名
		        names = methodSignature.getParameterNames(); 
			}else {
				LocalVariableTableParameterNameDiscoverer localVariableTableParameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
		        Object target = pjp.getTarget();//代理类
		        Method currentMethod = target.getClass().getMethod(methodSignature.getName(), methodSignature.getParameterTypes());
		        names = localVariableTableParameterNameDiscoverer.getParameterNames(currentMethod);
			}
			if(names == null) {
				throw new PlatformRuntimeException(pjp.getTarget().getClass().getName()+"$"+signature.getName()+"：未获取到参数名称列表");
			}
			
			Map<String, Object> map = new HashMap<>();
			if(names != null && names.length == args.length) {
				for (int i = 0; i < names.length; i++) {
					map.put(names[i], args[i]);
				}
			}
			return map;
		}
		
}
